/*
 * Galaxium Messenger
 * Copyright (C) 2008 Paul Burton <paulburton89@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Collections.Generic;

using Anculus.Core;

namespace Galaxium.Protocol.Msn
{
	public class SBBridge : AbstractMsnP2PBridge
	{
		const int _sbQueueSize = 10;
		
		SBConnection _sb = null;
		
		public override bool Open
		{
			get { return (_sb != null) && (_sb.Contacts.Count > 0) && _sb._authenticated; }
		}
		
		public override int MaxDataSize
		{
			get { return 1150; }
		}
		
		public override MsnContact Remote
		{
			get
			{
				if (_sb != null)
				{
					if (_sb.Contacts.Count == 1)
						return _sb.Contacts[0];
					if (_sb._inviteContacts.Length == 1)
						return _sb._inviteContacts[0];
				}
				
				foreach (MsnP2PSession p2pSession in SendQueues.Keys)
					return p2pSession.Remote;
				
				Log.Error ("Unable to find remote contact");
				
				return null;
			}
		}
		
		public SBConnection Switchboard
		{
			get { return _sb; }
		}
		
		public SBBridge (MsnP2PSession session)
			: base (_sbQueueSize)
		{
			SendQueues.Add (session, new MsnP2PSendQueue ());
		}
		
		public SBBridge (SBConnection sb)
			: base (_sbQueueSize)
		{
			_sb = sb;
			
			_sb.AddContentHandler<P2PContent> (SBP2PContentReceived);
			
			_sb.ContactJoined += SBContactJoined;
			_sb.ContactLeft += SBContactLeft;
			_sb.InviteContactFailed += SBInviteContactFailed;
			_sb.Closed += SBClosed;
		}
		
		public override void Send (MsnP2PSession session, MsnContact remote, P2PMessage msg)
		{
			if (_sb == null)
			{
				_sb = session.Session.GetSwitchboard (true, session.Remote);
				
				_sb.AddContentHandler<P2PContent> (SBP2PContentReceived);
				
				_sb.ContactJoined += SBContactJoined;
				_sb.ContactLeft += SBContactLeft;
				_sb.InviteContactFailed += SBInviteContactFailed;
				_sb.Closed += SBClosed;
			}
			
			base.Send (session, remote, msg);
		}
		
		protected override void TrueSend (MsnP2PSession session, MsnContact remote, P2PMessage msg)
		{
			if ((session != null) && (session.Remote != remote))
			{
				Log.Error ("!!!!!! SESSION REMOTE CONTACT != CONTACT !!!!!!");
				Log.Debug ("{0} vs {1}", session.Remote.UniqueIdentifier, remote.UniqueIdentifier);
				throw new ApplicationException ("Session Remote != Contact");
			}
			if (remote != _sb.Contacts[0])
			{
				Log.Error ("!!!!!! REMOTE != SWITCHBOARD CONTACT !!!!!!");
				Log.Debug ("{0} vs {1}", remote.UniqueIdentifier, _sb.Contacts[0].UniqueIdentifier);
				throw new ApplicationException ("Remote != Switchboard Contact");
			}
			
			P2PContent content = new P2PContent (msg.Session);
			content.Data = msg.ToByteArray (true);
			
			Log.Debug ("Sending MSG:\n"+msg.ToString(true));
			
			MSGCommand cmd = content.ToCommand<MSGCommand> ();
			cmd.MIMEHeader["P2P-Dest"] = _sb.Contacts[0].UniqueIdentifier;
			
			_sb.Send (cmd, delegate (IMsnCommand response)
			{
				if (!(response is ACKCommand))
				{
					// Try again
					TrueSend (session, remote, msg);
					return;
				}
				
				OnBridgeSent (new P2PMessageEventArgs (session, msg));
			});
		}
		
		void SBContactJoined (object sender, ContactEventArgs args)
		{
			if (_sb.Contacts.Count > 1)
				ClearSwitchboard ();
			else
				OnBridgeOpened ();
		}
		
		void SBContactLeft (object sender, ContactEventArgs args)
		{
			ClearSwitchboard ();
		}
		
		void SBInviteContactFailed (object sender, ContactEventArgs args)
		{
			ClearSwitchboard ();
		}
		
		void SBClosed (object sender, EventArgs args)
		{
			ClearSwitchboard ();
		}
		
		void SBP2PContentReceived (P2PContent content)
		{
			MsnP2PUtility.ProcessMessage (this, content.Source as MsnContact, content.P2PMessage);
		}
		
		void ClearSwitchboard ()
		{
			if (_sb == null)
				return;
			
			_sb.RemoveContentHandler<P2PContent> (SBP2PContentReceived);
			
			_sb.ContactJoined -= SBContactJoined;
			_sb.ContactLeft -= SBContactLeft;
			_sb.InviteContactFailed -= SBInviteContactFailed;
			_sb.Closed -= SBClosed;
			
			_sb = null;
			
			OnBridgeClosed ();
		}
	}
}
